package com.itfreer.gis.data.database;

import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.jdbc.JDBCDataStore;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.sql.SQLException;
import java.util.*;

/**
 * 定义基于GeoTools的数据库的要素操作类
 */
@Component
public class GeoToolsDataEditServer implements DatabaseDataEditServer {

    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;

    private JDBCDataStore dataStore;

    /**
     * 查询
     *
     * @param source
     * @param where  查询的依据 查询的值列表
     * @return
     */
    public List<SimpleFeature> queryFeature(String source, String where) throws Exception {
        List<SimpleFeature> features = new ArrayList<>();
        try {
            Filter filter = null;
            if (where != null) {
                filter = CQL.toFilter(where);
            }
            SimpleFeatureSource featureSource = getFeatureSource(source);
            SimpleFeatureCollection featureCollection = filter != null ? featureSource.getFeatures(filter)
                    : featureSource.getFeatures();
            SimpleFeatureIterator itertor = featureCollection.features();
            while (itertor.hasNext()) {
                SimpleFeature feature = itertor.next();
                features.add(feature);
            }
            itertor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return features;
    }

    /**
     * 查询
     *
     * @param source
     * @param option   查询的操作(相交，包含等)
     * @param geometry 查询空间条件
     * @return
     */
    public List<SimpleFeature> queryFeature(String source, String option, Geometry geometry) throws Exception {
        // 根据指定数据库源查询对应属性信息
        List<SimpleFeature> features = new ArrayList<>();
        try {
            String wkt = geometry.toText();
            Filter filter = null;
            if (option.equals("CONTAINS")) {
                filter = CQL.toFilter("CONTAINS(geom, " + wkt + ")");
            } else if (option.equals("INTERSECTS")) {
                filter = CQL.toFilter("INTERSECTS(geom, " + wkt + " )");
            }
            SimpleFeatureSource featureSource = getFeatureSource(source);
            SimpleFeatureCollection featureCollection = filter != null ? featureSource.getFeatures(filter)
                    : featureSource.getFeatures();
            SimpleFeatureIterator itertor = featureCollection.features();
            while (itertor.hasNext()) {
                SimpleFeature feature = itertor.next();
                features.add(feature);
            }
            itertor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return features;
    }

    /**
     * 新增
     *
     * @return
     */
    public Boolean addFeatures(String source, List<SimpleFeature> features, SimpleFeatureType metadata) throws Exception {
        // 连接对应数据源，添加数据
        SimpleFeatureSource featureSource = getFeatureSource(source);

        // 获取空间表空间属性字段
        SimpleFeatureType ftype = featureSource.getSchema();
        String geomFieldName = ftype.getGeometryDescriptor().getName().getLocalPart();
        List<SimpleFeature> featuresNew = new ArrayList<>();

        // 解决表空间字段为geom时的导入问题
        if (!geomFieldName.equals("the_geom")) {
            GeometryAttribute geom = features.get(0).getDefaultGeometryProperty();
            Collection<Property> propertys = features.get(0).getProperties();
            CoordinateReferenceSystem crs = geom.getDescriptor().getCoordinateReferenceSystem();
            SimpleFeatureType newType = createFeatureType(geom.getValue().getClass(), propertys, crs, geomFieldName);

            for (SimpleFeature sf : features) {
                SimpleFeatureBuilder sfb = new SimpleFeatureBuilder(newType);
                SimpleFeature simpleFeature = sfb.buildFeature(null);
                simpleFeature.setAttributes(sf.getAttributes());
                featuresNew.add(simpleFeature);
            }

        }

        Boolean result = true;
        Transaction transaction = new DefaultTransaction("add");
        if (featureSource instanceof SimpleFeatureStore) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
            SimpleFeatureCollection collection = null;
            if (!geomFieldName.equals("the_geom")) {
                collection = new ListFeatureCollection(ftype, featuresNew);
            } else {
                collection = new ListFeatureCollection(ftype, features);
            }
            featureStore.setTransaction(transaction);
            try {
                featureStore.addFeatures(collection);
                transaction.commit();
            } catch (Exception problem) {
                problem.printStackTrace();
                transaction.rollback();
            } finally {
                transaction.close();
            }
        }
        return result;
    }

    /**
     * 编辑
     *
     * @return
     */
    public SimpleFeature updateFeature(String source, SimpleFeature row, SimpleFeatureType metadata) throws Exception {
        return null;
    }

    /**
     * 删除
     *
     * @return
     */
    public Boolean deleteFeature(String source, String where) throws Exception {
        SimpleFeatureSource featureSource = getFeatureSource(source);
        Transaction transaction = new DefaultTransaction("delete");
        Boolean result = true;
        if (featureSource instanceof SimpleFeatureStore) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
            Filter filter = null;
            if (where != null) {
                filter = CQL.toFilter(where);
            }
            try {
                if (filter != null) {
                    featureStore.removeFeatures(filter);
                }
                transaction.commit();
            } catch (Exception problem) {
                problem.printStackTrace();
                transaction.rollback();
                result = false;
            } finally {
                transaction.close();
            }
        }
        return result;
    }

    @Override
    public SimpleFeatureType getTableMetadata(String source) {
        try {
            SimpleFeatureSource featureSource = getFeatureSource(source);
            return featureSource.getSchema();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建表结构,解决表空间字段为geom时的导入问题
     *
     * @throws FactoryException
     * @throws NoSuchAuthorityCodeException
     */
    private static SimpleFeatureType createFeatureType(Class<?> geoTypeClass, Collection<Property> propertys,
                                                       CoordinateReferenceSystem crs, String geomFieldName) throws NoSuchAuthorityCodeException, FactoryException {
        // 定义图形信息和属性信息
        SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
        tb.setCRS(crs);
        tb.setName("shapefile");
        for (Property item : propertys) {
            if (item.getName().toString().equals("the_geom")) {
                tb.add(geomFieldName, geoTypeClass);
            } else {
                tb.add(item.getName().toString(), item.getType().getBinding());
            }
        }
        // build the type
        final SimpleFeatureType ftype = tb.buildFeatureType();
        return ftype;
    }

    /**
     * 获取数据源
     *
     * @param source
     * @return
     * @throws SQLException
     * @throws IOException
     */
    private SimpleFeatureSource getFeatureSource(String source) throws SQLException, IOException {
        if (dataStore == null) {
            String urlCopy = url;
            urlCopy = urlCopy.replace("//", "").replace("/", ":");
            String[] msgs = urlCopy.split(":");
            Map<String, Object> params = new HashMap<>();
            params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
            params.put(PostgisNGDataStoreFactory.HOST.key, msgs[2]);
            params.put(PostgisNGDataStoreFactory.PORT.key, msgs[3]);
            params.put(PostgisNGDataStoreFactory.DATABASE.key, msgs[4]);
            params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");
            params.put(PostgisNGDataStoreFactory.USER.key, username);
            params.put(PostgisNGDataStoreFactory.PASSWD.key, password);
            dataStore = (JDBCDataStore) DataStoreFinder.getDataStore(params);
        }
        return dataStore.getFeatureSource(source);
    }
}
